﻿#include "PreWindowInitializer.h"

// VTK
#include <vtkCallbackCommand.h>
#include <vtkInteractorObserver.h>
#include <vtkCamera.h>
#include <vtkRenderWindow.h>
#include <vtkRenderer.h>
#include <vtkPointPicker.h>
#include <vtkCellPicker.h>
#include <vtkAreaPicker.h>
#include <vtkPlanes.h>
#include <vtkProp3DCollection.h>
#include <vtkPropPicker.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkImageActor.h>

#include "FITK_Component/FITKRenderWindowVTK/FITKGraphAreaPicker.h"
#include "FITK_Kernel/FITKAppFramework/FITKKeyMouseStates.h"

// App
#include "FITK_Kernel/FITKAppFramework/FITKAppFramework.h"
#include "FITK_Kernel/FITKAppFramework/FITKGlobalData.h"
#include "FITK_Kernel/FITKAppFramework/FITKComponents.h"
#include "FITK_Kernel/FITKAppFramework/FITKComponentInterface.h"

// Operator
#include "FITK_Kernel/FITKCore/FITKOperatorRepo.h"
#include "OperatorsInterface/GraphInteractionOperator.h"
#include "OperatorsInterface/GraphEventOperator.h"
 
// Render
#include "FITK_Component/FITKRenderWindowVTK/FITKGraph3DWindowVTK.h"
#include "FITK_Component/FITKRenderWindowVTK/FITKGraphRender.h"

// GUI
#include "GUIFrame/MainWindow.h"
#include "GUIFrame/CentralWidget.h"
#include "GUIWidget/GraphArea.h"
#include "GUIWidget/PreWindow3D.h"
#include "FITK_Component/FITKWidget/FITKMdiArea.h"

// Pick
#include "PickDataProvider/GUIPickInfo.h"

PreWindowInitializer::PreWindowInitializer()
{
    // 初始化渲染层数。
    this->setLayerCount(3);

    //背景颜色
    //QColor top = QColor::fromRgb(27, 45, 70);
    //QColor buttom = QColor::fromRgb(160, 174, 195);
    //this->setValue(BackGroundColorTop, top);
    //this->setValue(BackGroundColorButtom, buttom);
}

Comp::FITKGraphInteractionStyle* PreWindowInitializer::getStyle()
{
    return PreWindowInteractionStyle::New();
}

PreWindowInteractionStyle* PreWindowInteractionStyle::New()
{
    return new PreWindowInteractionStyle;
}

PreWindowInteractionStyle::PreWindowInteractionStyle()
{
    // 初始化定时器。
    m_timerForDoubleClick.setInterval(500);
    m_timerForDoubleClick.setSingleShot(true);
    connect(&m_timerForDoubleClick, &QTimer::timeout, this, &PreWindowInteractionStyle::slot_clearDoubleClickFlag);

    // 初始化操作器。
    //@{
    auto operatorRepo = Core::FITKOperatorRepo::getInstance();
    m_operPick = operatorRepo->getOperatorT<EventOper::GraphInteractionOperator>("GraphPick");
    m_operGraph = operatorRepo->getOperatorT<EventOper::GraphEventOperator>("GraphPreprocess");
    //@}
}

void PreWindowInteractionStyle::OnLeftButtonDown()
{
    // TEST.
    //@{
    //GUI::GUIPickInfoStru pickInfo = GUI::GUIPickInfo::GetPickInfo();
    //if (pickInfo._pickObjType == GUI::GUIPickInfo::PickObjType::POBJNone)
    //{
    //    pickInfo._pickObjType = GUI::GUIPickInfo::PickObjType::POBJAllExceptModel;
    //    pickInfo._pickMethod = GUI::GUIPickInfo::PickMethod::PMIndividually;
    //    pickInfo._autoClear = false;
    //}
    //GUI::GUIPickInfo::SetPickInfo(pickInfo);
    //@}

    this->Interactor->GetEventPosition(m_leftButtonDowmPos);

    if (m_areaPick != nullptr)
    {
        m_areaPick->setLeftButtonDownPos(m_leftButtonDowmPos);
        m_areaPick->enable(true);
    }
}

void PreWindowInteractionStyle::OnLeftButtonUp()
{
    this->Interactor->GetEventPosition(m_leftButtonUpPos);

    vtkInteractorStyleRubberBandPick::OnLeftButtonUp();

    GUI::GUIPickInfoStru pickInfo = GUI::GUIPickInfo::GetPickInfo();

    // 框选 & 结束绘制拖拽矩形。
    if (isMouseMoved())
    {
        if (m_areaPick)
        {
            if (pickInfo._pickMethod == GUI::GUIPickInfo::PickMethod::PMIndividually && m_areaPick->isEnable())
                m_areaPick->pick();
            m_areaPick->enable(false);
        }
    }
    // 点选。
    else
    {
        // 未开启拾取则跳出。
        if (pickInfo._pickObjType == GUI::GUIPickInfo::PickObjType::POBJNone)
        {
            // 只在未开启拾取模式下开启双击功能。
            //@{
            // 未检测到第一次拾取则修改标记并开启定时器。
            if (!m_hasFirstPick)
            {          
                m_hasFirstPick = true;
                m_timerForDoubleClick.start();
            }
            // 存在第一次点击标记则记为双击事件并执行双击拾取功能。
            else
            {
                m_hasFirstPick = false;

                Comp::FITKGraph3DWindowVTK* graphWindow = getCurrentGraphWindow();
                if (graphWindow)
                {
                    // 优先拾取图片。
                    if (!doublePick2D(graphWindow, m_leftButtonUpPos))
                    {
                        doublePickCell(graphWindow, m_leftButtonUpPos);
                    }
                }
            }
            //@}
        }
        // 否则执行点击拾取。
        else
        {
            pick();
        }
    }
}

void PreWindowInteractionStyle::OnMiddleButtonDown()
{
    this->Interactor->GetEventPosition(m_leftButtonDowmPos);

    if (FITKAPP->getGlobalData()->getKeyMouseStates()->keyPressed(Qt::Key_Shift))
        vtkInteractorStyleRubberBandPick::OnMiddleButtonDown();
    else
        vtkInteractorStyleRubberBandPick::OnLeftButtonDown();
}

void PreWindowInteractionStyle::OnMiddleButtonUp()
{
    this->Interactor->GetEventPosition(m_leftButtonDowmPos);

    vtkInteractorStyleRubberBandPick::OnLeftButtonUp();
    vtkInteractorStyleRubberBandPick::OnMiddleButtonUp();

    if (m_areaPick != nullptr)
    {
        m_areaPick->enable(false);
    }
}

void PreWindowInteractionStyle::OnMouseMove()
{
    vtkInteractorStyleRubberBandPick::OnMouseMove();

    this->Interactor->GetEventPosition(m_leftButtonUpPos);

    bool isMoved = this->isMouseMoved();   

    // 默认或关闭拾取模式下绘制橡皮筋。
    GUI::GUIPickInfo::PickMethod pm = GUI::GUIPickInfo::GetPickInfo()._pickMethod;
    bool needDrawRect = (pm == GUI::GUIPickInfo::PickMethod::PMIndividually ||
        pm == GUI::GUIPickInfo::PickMethod::PMNone);
    if (needDrawRect && isMoved)
    {
        const bool lbd = FITKAPP->getGlobalData()->getKeyMouseStates()->mousePressed(Qt::LeftButton);
        if (lbd)
        {
            m_areaPick->drawRectangle();
        }
    }
}

void PreWindowInteractionStyle::OnMouseWheelForward()
{
    vtkInteractorStyleRubberBandPick::OnMouseWheelForward();

    this->FindPokedRenderer(
        this->Interactor->GetEventPosition()[0], this->Interactor->GetEventPosition()[1]);
    if (this->CurrentRenderer == nullptr)
    {
        return;
    }

    this->GrabFocus(this->EventCallbackCommand);
    this->StartDolly();
    // double factor = this->MotionFactor * -0.2 * this->MouseWheelMotionFactor;
    this->Dolly(0.98); // pow(1.1, factor)
    this->EndDolly();
    this->ReleaseFocus();
}

void PreWindowInteractionStyle::OnMouseWheelBackward()
{
    vtkInteractorStyleRubberBandPick::OnMouseWheelBackward();

    this->FindPokedRenderer(
        this->Interactor->GetEventPosition()[0], this->Interactor->GetEventPosition()[1]);
    if (this->CurrentRenderer == nullptr)
    {
        return;
    }

    this->GrabFocus(this->EventCallbackCommand);
    this->StartDolly();
    // double factor = this->MotionFactor * 0.2 * this->MouseWheelMotionFactor;
    this->Dolly(1.05); // pow(1.1, factor)
    this->EndDolly();
    this->ReleaseFocus();
}

void PreWindowInteractionStyle::OnRightButtonDown()
{
    vtkInteractorStyleRubberBandPick::OnRightButtonDown();
    this->Interactor->GetEventPosition(m_leftButtonDowmPos);
}

void PreWindowInteractionStyle::OnRightButtonUp()
{
    vtkInteractorStyleRubberBandPick::OnRightButtonUp();
    this->Interactor->GetEventPosition(m_leftButtonUpPos);
}

void PreWindowInteractionStyle::Dolly(double factor)
{
    FITKGraphInteractionStyle::Dolly(factor);

    // 根据相机刷新可视化对象。
    if (m_operGraph)
    {
        m_operGraph->updateWithCameraChanged();
    }
}

void PreWindowInteractionStyle::Pan()
{
    FITKGraphInteractionStyle::Pan();

    // 根据相机刷新可视化对象。
    if (m_operGraph)
    {
        m_operGraph->updateWithCameraChanged();
    }
}

void PreWindowInteractionStyle::Spin()
{
    FITKGraphInteractionStyle::Spin();

    // 根据相机刷新可视化对象。
    if (m_operGraph)
    {
        m_operGraph->updateWithCameraChanged();
    }
}

void PreWindowInteractionStyle::Rotate()
{
    FITKGraphInteractionStyle::Rotate();

    // 根据相机刷新可视化对象。
    if (m_operGraph)
    {
        m_operGraph->updateWithCameraChanged();
    }
}

void PreWindowInteractionStyle::areaPick(int* startPos, int* endPos)
{
    // 获取当前三维窗口。
    Comp::FITKGraph3DWindowVTK* graphWindow = getCurrentGraphWindow();
    if (!graphWindow || !m_operPick)
    {
        return;
    }

    // 所有渲染层均执行一次框选并获取拾取演员。
    //@{
    QList<vtkActor*> actors;
    vtkSmartPointer<vtkAreaPicker> areaPicker = vtkSmartPointer<vtkAreaPicker>::New();

    for (int i = 0; i < graphWindow->getRenderCount(); i++)
    {
        vtkRenderer* renderer = graphWindow->getRenderer(i)->getRenderer();
        areaPicker->AreaPick(startPos[0], startPos[1], endPos[0], endPos[1], renderer);
        vtkProp3DCollection* props = areaPicker->GetProp3Ds();
        props->InitTraversal();

        const int nActors = props->GetNumberOfItems();
        for (vtkIdType i = 0; i < nActors; i++)
        {
            vtkProp3D* prop = props->GetNextProp3D();
            vtkActor* actor = vtkActor::SafeDownCast(prop);
            if (actor)
            {
                actors.append(actor);
            }
        }
    }
    //@}  

    // 没有拾取到，且不是Ctrl拾取模式则清除拾取。
    AppFrame::FITKKeyMouseStates* settings = FITKGLODATA->getKeyMouseStates();
    bool ctrlPick = settings->keyPressed(Qt::Key::Key_Control);
    if (actors.isEmpty() && !ctrlPick)
    {
        if (GUI::GUIPickInfo::GetPickInfo()._autoClear)
        {
            m_operPick->clear(graphWindow);
        }

        return;
    }

    vtkPlanes* pickPlanes = areaPicker->GetFrustum();

    // 执行拾取功能。
    m_operPick->picked(graphWindow, actors, pickPlanes);
}

void PreWindowInteractionStyle::pick()
{
    if (!m_operPick)
    {
        return;
    }

    // 获取当前鼠标位置进行拾取。
    vtkRenderWindowInteractor* interactor = this->GetInteractor();
    if (!interactor)
    {
        return;
    }

    Comp::FITKGraph3DWindowVTK* graphWindow = getCurrentGraphWindow();
    if (!graphWindow)
    {
        return;
    }

    int* pos = interactor->GetEventPosition();

    bool hasPicked = false;

    // 获取当前拾取类型。
    auto type = GUI::GUIPickInfo::GetPickInfo()._pickObjType;
    switch (type)
    {
    case GUI::GUIPickInfo::POBJAll:
    case GUI::GUIPickInfo::POBJAllExceptModel:
    {
        // 首先进行图片拾取。
        hasPicked = pick2D(graphWindow, pos);
        if (hasPicked)
        {
            break;
        }

        // 未拾取到图片则进行单元拾取。
        hasPicked = pickCell(graphWindow, pos, 0.01);

        break;
    }
    case GUI::GUIPickInfo::POBJGeometryVert:
    case GUI::GUIPickInfo::POBJGeometryEdge:
    case GUI::GUIPickInfo::POBJGeometryFace:
    case GUI::GUIPickInfo::POBJGeometrySolid:
    case GUI::GUIPickInfo::POBJGeometry:
    case GUI::GUIPickInfo::POBJMeshCell:
    case GUI::GUIPickInfo::POBJMeshCell1D:
    case GUI::GUIPickInfo::POBJMeshCell2D:
    case GUI::GUIPickInfo::POBJMeshCell3D:
    case GUI::GUIPickInfo::POBJSEASubSystem:   
    case GUI::GUIPickInfo::POBJSEABeam:
    case GUI::GUIPickInfo::POBJSEAShell:
    case GUI::GUIPickInfo::POBJSEACavity:
    case GUI::GUIPickInfo::POBJSEAShellAndCavity: 
    case GUI::GUIPickInfo::POBJJunctions:
    case GUI::GUIPickInfo::POBJSEAJunctions:
    case GUI::GUIPickInfo::POBJSEAJunctionPoint:
    case GUI::GUIPickInfo::POBJSEAJunctionLine:
    case GUI::GUIPickInfo::POBJSEAJunctionArea:
    case GUI::GUIPickInfo::POBJLoadAll:
    case GUI::GUIPickInfo::POBJFEFace:
    case GUI::GUIPickInfo::POBJFEStructure:
    case GUI::GUIPickInfo::POBJFEAcoustic:
    case GUI::GUIPickInfo::POBJSubSystem:
    case GUI::GUIPickInfo::POBJSubSysOutBeamSemiFEFaceSensor:
    {
        hasPicked = pickCell(graphWindow, pos);
        break;
    }
    case GUI::GUIPickInfo::POBJDiscNode:
    {
        // 扩大拾取精度，增加拾取节点概率。
        hasPicked = pickCell(graphWindow, pos, 0.001);
        break;
    }
    case GUI::GUIPickInfo::POBJSEASemiInfFluid:
    {
        // 首先进行图片拾取。
        hasPicked = pick2D(graphWindow, pos);
        if (!hasPicked)
        {
            // 未拾取到图片则进行单元拾取。
            hasPicked = pickCell(graphWindow, pos);
        }

        break;
    }
    default:
        return;
    }

    // 未拾取到则清除拾取。
    if (!hasPicked && GUI::GUIPickInfo::GetPickInfo()._autoClear)
    {
        m_operPick->clear(graphWindow);
    }
}

bool PreWindowInteractionStyle::pickPoint(Comp::FITKGraph3DWindowVTK* graphWindow, int* pos, double tol)
{
    // 节点拾取器。
    vtkSmartPointer<vtkCellPicker> picker = vtkSmartPointer<vtkCellPicker>::New();

    picker->SetTolerance(tol);

    // 优先拾取最后一层，反向遍历。
    for (int i = graphWindow->getRenderCount() - 1; i >= 0; i--)
    {
        vtkRenderer* renderer = graphWindow->getRenderer(i)->getRenderer();

        int ret = picker->Pick(pos[0], pos[1], 0, renderer);

        // 是否拾取到对象。
        if (!ret)
        {
            continue;
        }

        // 获取拾取节点索引。
        vtkActor* actor = picker->GetActor();
        int index = picker->GetPointId();
        if (index < 0)
        {
            continue;
        }

        double* pickedWorldPos = picker->GetPickPosition();

        // 执行拾取数据处理。
        m_operPick->picked(graphWindow, actor, index, pickedWorldPos);

        return true;
    } 

    return false;
}

bool PreWindowInteractionStyle::pickCell(Comp::FITKGraph3DWindowVTK* graphWindow, int* pos, double tol)
{
    // 单元拾取器。
    vtkSmartPointer<vtkCellPicker> picker = vtkSmartPointer<vtkCellPicker>::New();

    picker->SetTolerance(tol);

    // 优先拾取最后一层，反向遍历。
    for (int i = graphWindow->getRenderCount() - 1; i >= 0 ; i--)
    {
        vtkRenderer* renderer = graphWindow->getRenderer(i)->getRenderer();

        // 优先进行顶层拾取。
        int ret = picker->Pick(pos[0], pos[1], 0, renderer);

        // 是否拾取到对象。
        if (!ret)
        {
            continue;
        }

        vtkProp* prop = picker->GetViewProp();

        // 图片标签。
        vtkImageActor* actorImg = vtkImageActor::SafeDownCast(prop);
        if (actorImg)
        {
            // 执行拾取数据处理。
            m_operPick->picked(graphWindow, actorImg);
            return true;
        }

        // 获取拾取单元索引。
        vtkActor* actor = vtkActor::SafeDownCast(prop);
        int index = picker->GetCellId();
        if (index < 0)
        {
            continue;
        }

        double* pickedWorldPos = picker->GetPickPosition();

        // 执行拾取数据处理。
        m_operPick->picked(graphWindow, actor, index, pickedWorldPos);
       
        return true;
    }

    return false;
}

bool PreWindowInteractionStyle::pick2D(Comp::FITKGraph3DWindowVTK* graphWindow, int* pos)
{
    // 对象拾取器。
    vtkSmartPointer<vtkPropPicker> pickerProp = vtkSmartPointer<vtkPropPicker>::New();

    // 优先拾取最后一层，反向遍历。
    for (int i = graphWindow->getRenderCount() - 1; i >= 0; i--)
    {
        vtkRenderer* renderer = graphWindow->getRenderer(i)->getRenderer();

        // 优先进行特殊标签与符号拾取。
        int ret = pickerProp->Pick(pos[0], pos[1], 0, renderer);

        // 必须重绘，否则会导致黑屏。
        renderer->GetRenderWindow()->Render();

        vtkProp* prop = pickerProp->GetViewProp();
        Q_UNUSED(ret);

        // 文本标签。
        vtkActor2D* actor2D = vtkActor2D::SafeDownCast(prop);
        if (actor2D)
        {
            // 执行拾取数据处理。
            m_operPick->picked(graphWindow, actor2D);
            return true;
        }
    }

    return false;
}

bool PreWindowInteractionStyle::doublePickCell(Comp::FITKGraph3DWindowVTK* graphWindow, int* pos, double tol)
{
    // 单元拾取器。
    vtkSmartPointer<vtkCellPicker> picker = vtkSmartPointer<vtkCellPicker>::New();

    picker->SetTolerance(tol);

    // 优先拾取最后一层，反向遍历。
    for (int i = graphWindow->getRenderCount() - 1; i >= 0; i--)
    {
        vtkRenderer* renderer = graphWindow->getRenderer(i)->getRenderer();

        // 优先进行顶层拾取。
        int ret = picker->Pick(pos[0], pos[1], 0, renderer);

        // 是否拾取到对象。
        if (!ret)
        {
            continue;
        }

        vtkProp* prop = picker->GetViewProp();

        // 图片标签。
        vtkImageActor* actorImg = vtkImageActor::SafeDownCast(prop);
        if (actorImg)
        {
            // 执行双击拾取数据处理。
            m_operPick->doublePicked(actorImg);
            return true;
        }

        // 获取拾取单元索引。
        vtkActor* actor = vtkActor::SafeDownCast(prop);
        int index = picker->GetCellId();
        if (index < 0)
        {
            continue;
        }

        // 执行拾取数据处理。
        m_operPick->doublePicked(actor, index);

        return true;
    }

    return false;
}

bool PreWindowInteractionStyle::doublePick2D(Comp::FITKGraph3DWindowVTK* graphWindow, int* pos)
{
    // 对象拾取器。
    vtkSmartPointer<vtkPropPicker> pickerProp = vtkSmartPointer<vtkPropPicker>::New();

    // 优先拾取最后一层，反向遍历。
    for (int i = graphWindow->getRenderCount() - 1; i >= 0; i--)
    {
        vtkRenderer* renderer = graphWindow->getRenderer(i)->getRenderer();

        // 优先进行特殊标签与符号拾取。
        int ret = pickerProp->Pick(pos[0], pos[1], 0, renderer);

        // 必须重绘，否则会导致黑屏。
        renderer->GetRenderWindow()->Render();

        vtkProp* prop = pickerProp->GetViewProp();
        Q_UNUSED(ret);

        // 文本标签。
        vtkActor2D* actor2D = vtkActor2D::SafeDownCast(prop);
        if (actor2D)
        {
            // 执行双击拾取数据处理。
            m_operPick->doublePicked(actor2D);
            return true;
        }
    }

    return false;
}

void PreWindowInteractionStyle::slot_clearDoubleClickFlag()
{
    m_hasFirstPick = false;
}

Comp::FITKGraph3DWindowVTK* PreWindowInteractionStyle::getCurrentGraphWindow()
{
    // 获取主窗口。
    GUI::MainWindow* mainWindow = FITKAPP->getGlobalData()->getMainWindowT<GUI::MainWindow>();
    if (!mainWindow)
    {
        return nullptr;
    }

    // 获取中心区域。
    GUI::CentralWidget* centralWidget = mainWindow->getCentralWidget();
    if (!centralWidget)
    {
        return nullptr;
    }

    // 获取3D区域。
    GUI::GraphArea* gArea = centralWidget->getGraphArea();
    if (!gArea)
    {
        return nullptr;
    }

    GUI::PreWindow3D* widget3D = nullptr;

    widget3D = gArea->getCurrentWindow<GUI::PreWindow3D>();
    if (widget3D == nullptr) {
        widget3D = dynamic_cast<GUI::PreWindow3D*>(gArea->getFirstWindow(GUI::GraphAbstractSubWin::SubWinType::Pre3D));
        if (widget3D == nullptr){
            return nullptr;
        }
    }

    //获取3D可视化区域
    Comp::FITKGraph3DWindowVTK* graphWidget = widget3D->getRenderWindow();
    if (!graphWidget)
    {
        return nullptr;
    }

    return graphWidget;
}